-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: [v0.8-develop] Add allowlist sample hook, refactor test base #70
Conversation
override | ||
returns (uint256) | ||
{ | ||
if (functionId == uint8(FunctionId.PRE_VALIDATION_HOOK)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the point of this check, and the similar one in the runtime validation hook?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In other plugin examples we've been using an if-else chain for the internal function dispatcher, so I was following that pattern here too. I suppose we could cut it and just treat every function id the same here, because this one doesn't have multiple implementations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like the idea of not having the functionId, especially the plugins are global singleton.
Actually, we should be explicit since we do ask the functionId
var to be passed.
} | ||
|
||
// solhint-disable-next-line no-empty-blocks | ||
function pluginManifest() external pure override returns (PluginManifest memory) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this blank because it's supposed to only be installed with a user-provided config?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, basically. The manifest is empty because this plugin doesn't define anything that the manifest can install:
- execution functions
- execution hooks
- validation functions
- signature validators
- call permissions
62344f2
to
0e67b55
Compare
b8637b4
to
4c147bf
Compare
0e67b55
to
c2da4d0
Compare
cd1637f
to
8c7133c
Compare
); | ||
|
||
vm.deal(address(account1), 100 ether); | ||
_customValidationSetup(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do the old tests here test allowList fails?
Are the error thrown from AllowlistPlugin
captured as AA errors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, depending on the call path, they throw either user op or runtime errors, and the fuzzer expects them to be called.
The error to look for is loaded from here:
reference-implementation/test/samples/AllowlistPlugin.t.sol
Lines 142 to 199 in 8c7133c
function _getExpectedUserOpError(Call[] memory calls) internal view returns (bytes memory) { | |
for (uint256 i = 0; i < calls.length; i++) { | |
Call memory call = calls[i]; | |
(bool allowed, bool hasSelectorAllowlist) = | |
allowlistPlugin.targetAllowlist(call.target, address(account1)); | |
if (allowed) { | |
if ( | |
hasSelectorAllowlist | |
&& !allowlistPlugin.selectorAllowlist(call.target, bytes4(call.data), address(account1)) | |
) { | |
return abi.encodeWithSelector( | |
IEntryPoint.FailedOpWithRevert.selector, | |
0, | |
"AA23 reverted", | |
abi.encodeWithSelector(AllowlistPlugin.SelectorNotAllowed.selector) | |
); | |
} | |
} else { | |
return abi.encodeWithSelector( | |
IEntryPoint.FailedOpWithRevert.selector, | |
0, | |
"AA23 reverted", | |
abi.encodeWithSelector(AllowlistPlugin.TargetNotAllowed.selector) | |
); | |
} | |
} | |
return ""; | |
} | |
function _getExpectedRuntimeError(Call[] memory calls) internal view returns (bytes memory) { | |
for (uint256 i = 0; i < calls.length; i++) { | |
Call memory call = calls[i]; | |
(bool allowed, bool hasSelectorAllowlist) = | |
allowlistPlugin.targetAllowlist(call.target, address(account1)); | |
if (allowed) { | |
if ( | |
hasSelectorAllowlist | |
&& !allowlistPlugin.selectorAllowlist(call.target, bytes4(call.data), address(account1)) | |
) { | |
return abi.encodeWithSelector( | |
UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, | |
address(allowlistPlugin), | |
uint8(AllowlistPlugin.FunctionId.PRE_VALIDATION_HOOK), | |
abi.encodeWithSelector(AllowlistPlugin.SelectorNotAllowed.selector) | |
); | |
} | |
} else { | |
return abi.encodeWithSelector( | |
UpgradeableModularAccount.PreRuntimeValidationHookFailed.selector, | |
address(allowlistPlugin), | |
uint8(AllowlistPlugin.FunctionId.PRE_VALIDATION_HOOK), | |
abi.encodeWithSelector(AllowlistPlugin.TargetNotAllowed.selector) | |
); | |
} | |
} |
And the test expects them here:
vm.expectRevert(expectedRevertData); |
vm.expectRevert(expectedRevertData); |
64a9917
to
23cdc39
Compare
8c7133c
to
d48a9a4
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
23cdc39
to
4599a2d
Compare
d48a9a4
to
9bc6789
Compare
4599a2d
to
8a09d31
Compare
9bc6789
to
17906b1
Compare
8a09d31
to
d402361
Compare
17906b1
to
e3ab9a2
Compare
d402361
to
60916fa
Compare
e3ab9a2
to
ae5c79a
Compare
function pluginManifest() external pure override returns (PluginManifest memory) {} | ||
|
||
function _checkAllowlistCalldata(bytes calldata callData) internal view { | ||
if (bytes4(callData[:4]) == IStandardExecutor.execute.selector) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Callout: when executeUserOp is used, we need to check calldata[4:8] instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this hook doesn't depend on the contents of executeUserOp
, then the account shouldn't send the user op, right? I guess this depends on how we do it, but it seems like the account should know whether to use msg.data
or userOp.callData
depending on the hook config.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah agree that it should be abstracted from plugins. in the current permissions stack, we directly send over userOp.callData which could be abi.encodeCall(...)
or abi.encodePacked(executeUserOp.selector, abi.encodeCall(...))
for hooks that don't require UO context, and for hooks that require UO context, they would always receive abi.encodePacked(executeUserOp.selector, abi.encodeCall(...))
. Dislike that part and would likely change that though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Additionally, we developed a 6900 address book plugin that utilized the pre-validation hook.
ae5c79a
to
5785dba
Compare
2afe76b
to
94b7592
Compare
5785dba
to
2a965ac
Compare
94b7592
to
e118421
Compare
2a965ac
to
4e0fdc3
Compare
e118421
to
94a3764
Compare
4e0fdc3
to
18abe0f
Compare
94a3764
to
e4526cc
Compare
18abe0f
to
17ed4e0
Compare
e4526cc
to
68a5847
Compare
17ed4e0
to
78ae1eb
Compare
68a5847
to
b579102
Compare
78ae1eb
to
5c10b0d
Compare
Motivation
To provide better context for how pre-validation hooks should be used, it would help to provide some real use cases. A simple case is an allowlist plugin - limiting the addresses, and possibly the selectors, of the targets of
execute
andexecuteBatch
when using a specific validation function.Solution
AccountTestBase
.AccountTestBase
calledCustomValidationTestBase
, and update the Per-hook data test (and the allowlist test) to use this.